package cn.schoolwow.quickserver.handler;

import cn.schoolwow.quickserver.domain.Client;
import cn.schoolwow.quickserver.domain.HttpResponseMeta;
import cn.schoolwow.quickserver.response.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.*;
import java.util.zip.GZIPOutputStream;

/**
 * http响应处理
 */
public class HttpResponseHandler implements Handler {
    private static Logger logger = LoggerFactory.getLogger(HttpResponseHandler.class);

    @Override
    public Handler handle(Client client) throws IOException {
        beforeResponse(client);

        HttpResponseMeta httpResponseMeta = client.httpResponseMeta;
        StringBuilder builder = new StringBuilder();
        builder.append(httpResponseMeta.protocol + " " + httpResponseMeta.status + " " + httpResponseMeta.statusMessage + "\r\n");
        Set<Map.Entry<String, List<String>>> entrySet = httpResponseMeta.headers.entrySet();
        for (Map.Entry<String, List<String>> entry : entrySet) {
            for (String value : entry.getValue()) {
                builder.append(entry.getKey() + ": " + value + "\r\n");
            }
        }
        builder.append("\r\n");

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        baos.write(builder.toString().getBytes());
        if(httpResponseMeta.bodyStream.size()>0){
            httpResponseMeta.bodyStream.writeTo(baos);
        }
        try {
            baos.writeTo(client.socket.getOutputStream());
        }catch (Exception e){
            e.printStackTrace();
            System.out.println(builder.toString());
        }
        client.socket.getOutputStream().flush();
        return null;
    }

    /**
     * 响应内容返回前
     */
    private void beforeResponse(Client client) {
        if (client.httpResponseMeta.status == 0) {
            client.httpResponseMeta.httpStatus(HttpStatus.OK);
        }
        gzip(client);
        if (client.httpResponseMeta.bodyStream.size()>0) {
            client.httpResponseMeta.headers.put("Content-Length", Arrays.asList(client.httpResponseMeta.bodyStream.size() + ""));
            client.httpResponseMeta.headers.put("Content-Type", Arrays.asList(client.httpResponseMeta.contentType + "; charset=" + client.httpResponseMeta.charset));
        }
        client.httpResponseMeta.headers.put("Date", Arrays.asList(new Date().toString()));
        client.httpResponseMeta.headers.put("Server", Arrays.asList("QuickServer2"));
        client.httpResponseMeta.headers.put("Connection", Arrays.asList("close"));
    }

    /**
     * 处理gzip压缩
     */
    private void gzip(Client client) {
        if (client.httpResponseMeta.bodyStream.size()==0) {
            logger.trace("[gzip压缩]无body内容,直接返回");
            return;
        }
        //判断客户端是否支持gzip压缩
        if (!client.httpRequestMeta.headers.containsKey("Accept-Encoding") || !client.httpRequestMeta.headers.get("Accept-Encoding").get(0).contains("gzip")) {
            //客户端不支持gip压缩
            logger.trace("[gzip压缩]客户端不支持gzip压缩");
            return;
        }
        //206状态码不进行gzip压缩
        if(client.httpResponseMeta.status==206||client.httpResponseMeta.headers.containsKey("Accept-Ranges")){
            logger.trace("[gzip压缩]分段下载不开启gzip压缩");
            return;
        }
        //大于2kb且指定类型文件进行gzip压缩
        if (client.httpResponseMeta.bodyStream.size() > 2048) {
            if (null == client.httpResponseMeta.contentType
                    || client.httpResponseMeta.contentType.startsWith("text/")
                    || client.httpResponseMeta.contentType.startsWith("application/")
                    || client.httpResponseMeta.contentType.startsWith("image/")
            ) {
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(baos)) {
                    client.httpResponseMeta.bodyStream.writeTo(gzipOutputStream);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                client.httpResponseMeta.bodyStream = baos;
                client.httpResponseMeta.headers.put("Content-Encoding", Arrays.asList("gzip"));
            }
        }
    }
}
